package cn.b.sky.admin.service.impl;

import cn.b.sky.common.exception.ImeException;
import cn.b.sky.common.exception.InfoException;
import cn.b.sky.common.utils.DatabaseUtils;
import cn.b.sky.admin.constants.SystemConstant;
import cn.b.sky.admin.dao.AdminTimedJobDao;
import cn.b.sky.admin.exception.ManagementException;
import cn.b.sky.admin.model.BatchJobReport;
import cn.b.sky.admin.model.Page;
import cn.b.sky.admin.model.condition.TimeJobCondition;
import cn.b.sky.admin.service.AdminTimedJobService;
import cn.b.sky.schedule.job.TimeJobUtils;
import cn.b.sky.schedule.model.TimedJobInfo;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

@Service("adminTimedJobService")
public class AdminTimedJobServiceImpl implements AdminTimedJobService {

  private static final Logger LOG = LoggerFactory.getLogger(AdminTimedJobServiceImpl.class);

  @Resource
  private AdminTimedJobDao adminTimedJobDao;

  @Resource
  private Scheduler scheduler;

  @Override
  public Page selectTimedJobList(TimeJobCondition timedJobInfo) {
    if (StringUtils.isBlank(timedJobInfo.getOrderField())) {
      timedJobInfo.setOrderField("jobCode");
    }
    timedJobInfo.setData(adminTimedJobDao.selectTimedJobList(timedJobInfo));
    timedJobInfo.setTotalCount(adminTimedJobDao.selectTomeJobCount(timedJobInfo));
    return timedJobInfo;
  }

  @Override
  public List<TimedJobInfo> selectEnableTimedJob(int enable) {
    return adminTimedJobDao.selectEnableTimedJob(enable);
  }

  @Override
  public TimedJobInfo selectTimedJob(String jobCode) {
    return adminTimedJobDao.selectTimedJob(jobCode);
  }

  @Override
  public void doEnableTimedJob(TimedJobInfo job) throws RuntimeException {
    try {
      // 修改定时作业状态启用
      job.setEnableFlag(SystemConstant.COMMON_STATUS_ON);
      adminTimedJobDao.updateTimedJob(job);

      JobDetail jobDetail = scheduler.getJobDetail(new JobKey(job.getJobCode(), Scheduler.DEFAULT_GROUP));
      // 新建JobDetail
      if (null == jobDetail) {

        // 新建JobDetail
        jobDetail = TimeJobUtils.getJobDetail(job);
        // 新建定时器
        CronTriggerFactoryBean cornTrigger = TimeJobUtils.getTriggerBean(job);
        // 设置JobDetail，定时表达式
        cornTrigger.setJobDetail(jobDetail);
        // 加入调度器
        scheduler.addJob(jobDetail, true);
        scheduler.scheduleJob(cornTrigger.getObject());
      }
    } catch (Exception e) {
      LOG.error("", e);
    }
  }

  @Override
  public void doDisableTimedJob(TimedJobInfo job) throws RuntimeException {
    try {
      if (null == job) {
        throw ImeException.newException(cn.b.sky.common.constants.SystemConstant.PARAMETERNULL);
      }
      // 修改定时作业状态 禁用
      job.setEnableFlag(cn.b.sky.common.constants.SystemConstant.COMMON_STATUS_OFF);
      adminTimedJobDao.updateTimedJob(job);
      JobKey key = new JobKey(job.getJobCode(), Scheduler.DEFAULT_GROUP);
      JobDetail jobDetail = scheduler.getJobDetail(key);
      // 新建JobDetail
      if (null != jobDetail) {
        scheduler.deleteJob(key);
      }
    } catch (Exception e) {
      throw new InfoException(e);
    }
  }

  @Override
  public void updateTimedConfig(TimedJobInfo job) {
    try {
      TimedJobInfo oldJob = selectTimedJob(job.getJobCode());
      // 持久化到数据库
      adminTimedJobDao.updateTimedJob(job);
      if (!oldJob.getEnableFlag().equals(job.getEnableFlag())) {
        JobKey key = new JobKey(job.getJobCode(), Scheduler.DEFAULT_GROUP);
        //禁用则删除定时任务
        if (job.getEnableFlag() == SystemConstant.COMMON_STATUS_OFF) {
          JobDetail jobDetail = scheduler.getJobDetail(key);
          // 新建JobDetail
          if (null != jobDetail) {
            scheduler.deleteJob(key);
          }
        } else if (job.getEnableFlag() == SystemConstant.COMMON_STATUS_ON) {
          addJobToScheduler(job);
        }
      }
      //改变表达式
      if (!oldJob.getCronExpression().equals(job.getCronExpression())) {
        //重新载入job
        rescheduleJob(job);
      }
    } catch (SchedulerException e) {
      throw ManagementException.newException("作业不存在!", e);
    } catch (ClassNotFoundException e) {
      throw ManagementException.newException("作业类不存在!", e);
    } catch (ParseException e) {
      throw ManagementException.newException("作业表达式有误!", e);
    } catch (NoSuchMethodException e) {
      throw ManagementException.newException("定时作业可执行的方法不存在", e);
    }
  }

  private void rescheduleJob(TimedJobInfo job) throws SchedulerException, ParseException {
    CronTriggerFactoryBean cronTrigger = TimeJobUtils.getTriggerBean(job);
    // 新建JobDetail
    if (null != cronTrigger) {
      // 设置定时表达式
      cronTrigger.setCronExpression(job.getCronExpression());
      if (!StringUtils.isBlank(job.getJavaTimeZone())) {
        cronTrigger.setTimeZone(TimeZone.getTimeZone(job.getJavaTimeZone()));
      }
      // 重新加入调度器
      scheduler.rescheduleJob(new TriggerKey(job.getJobCode(), Scheduler.DEFAULT_GROUP), cronTrigger.getObject());
    }
  }

  @Override
  public void doExecuteTimedJob(TimedJobInfo job) throws Exception {
    try {
      // 得到JOB类
      Class<?> jobClazz = Class.forName(job.getJobClass());
      // 得到执行方法
      Method executeManual = jobClazz.getDeclaredMethod("executeManual", TimedJobInfo.class);
      // 执行
      executeManual.invoke(jobClazz.newInstance(), job);
    } catch (ClassNotFoundException e) {
      throw ManagementException.newException("作业类不存在!", e);
    }
  }

  @Override
  public void updateLastExecTime(String jobCode) {
    TimedJobInfo job = adminTimedJobDao.selectTimedJob(jobCode);
    if (null != job) {
      job.setLastExecutionTime(new Date());
      adminTimedJobDao.updateTimedJob(job);
    }
  }

  @Override
  public void saveTimedConfig(TimedJobInfo job) {
    if (job == null || StringUtils.isBlank(job.getJobCode())) {
      return;
    }
    adminTimedJobDao.insertTimedConfig(job);
    if (job.getEnableFlag() == SystemConstant.COMMON_STATUS_ON) {
      try {
        addJobToScheduler(job);
      } catch (SchedulerException e) {
        throw ManagementException.newException("作业不存在!", e);
      } catch (ClassNotFoundException e) {
        throw ManagementException.newException("作业类不存在!", e);
      } catch (ParseException e) {
        throw ManagementException.newException("作业表达式有误!", e);
      } catch (NoSuchMethodException e) {
        throw ManagementException.newException("定时作业可执行的方法不存在", e);
      }
    }
  }

  private void addJobToScheduler(TimedJobInfo job) throws SchedulerException, ClassNotFoundException, ParseException,
      NoSuchMethodException {
    JobDetail jobDetail = scheduler.getJobDetail(new JobKey(job.getJobCode(), Scheduler.DEFAULT_GROUP));
    // 新建JobDetail
    if (null == jobDetail) {
      jobDetail = TimeJobUtils.getJobDetail(job);
      // 新建定时器
      CronTriggerFactoryBean cornTrigger = TimeJobUtils.getTriggerBean(job);
      // 设置JobDetail，定时表达式
      cornTrigger.setJobDetail(jobDetail);
      // 加入调度器
      scheduler.addJob(jobDetail, true);
      scheduler.scheduleJob(cornTrigger.getObject());
    } else {
      rescheduleJob(job);
    }
  }

  @Override
  public boolean checkTimedJob(String jobCode) {
    return adminTimedJobDao.checkTimedJob(jobCode) >= 1;
  }

  @Override
  public BatchJobReport deleteTimedJob(String jobCode) {    
    String cause = "";
    Boolean success = true;   
    try {
      adminTimedJobDao.deleteTimedJob(jobCode);
      JobDetail jobDetail;
      jobDetail = scheduler.getJobDetail(new JobKey(jobCode, Scheduler.DEFAULT_GROUP));
      if (null != jobDetail) {
        scheduler.deleteJob(jobDetail.getKey());
      }
    } catch (SchedulerException e) {
      LOG.error("{}", e);
      cause = "移除失败";
      success = false;
    }catch (Exception e) {
      LOG.error("{}", e);
      if (DatabaseUtils.isConstraintsError(e.getMessage())) {
        // 外键删除已存在提示
        cause = "数据已被使用,不能删除!";
      } else {
        cause = e.getMessage();
      }
      success = false;
    }
    return new BatchJobReport(jobCode, cause, success, "zh"); 
  }
}
